home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PsL Monthly 1993 December
/
PSL Monthly Shareware CD-ROM (December 1993).iso
/
prgmming
/
dos
/
c
/
bump.exe
/
BUMP.DOC
< prev
next >
Wrap
Text File
|
1991-02-10
|
20KB
|
407 lines
BUMP 1.1
A Beginner's Understandable Matrix Package
in Borland C++
From the dawn of programming, people have wanted to be able to write
something like
Matrix A(4,4), B(4,4), C(4,4), D(4,4);
...
A = (B + C)* D;
and get in the A matrix the result expected from matrix algebra. The best
that languages like Fortran, Basic, Pascal, or C could offer, however, was
either a lot of function calls or loops on subscripts. Special matrix
interpreters were developed which, however, limited what you could do. If
what you wanted to do, say graph the diagonal of a matrix, couldn't be done
by your package, you were stuck. Also, you frequently had to master large
manuals for one special-purpose program. With C++, however, it is
possible for the user to define a data type "Matrix" and then write
functions to "overload" the =, +, and * operators so that when the compiler
recognizes that, say, a + is between two matrices, it will call the user-
written function to add the two matrices. At the same time, you have all
the flexibility of C and C++ and very little to learn in the way of
special-purpose instructions. It is just a matter of writing the
necessary code to define the Matrix and Vector data types and to overload
the operators.
Since matrices are so widely used, I had expected that it would be easy to
find examples in books of exactly how to define the data types and write
the overloaded operator functions. I found hints and fragments, but no
fully worked out satisfactory solution. In particular, the examples in the
books tended to be bad about loosing track of memory that had been
allocated for temporary matrices (like (B + C) in the above example), so
that repeated use of equations like the one above would eventually stop the
program. Design of the destructors was of the essence for this problem,
and it gets scant attention in most introductions to C++. On the bulletin
boards, I found two extensive examples at the opposite extreme, Newmat and
Yamp. They were too complicated to serve as examples. I learned some
things from them but could not begin to really follow how they worked. So
I set out on my own to see if I could apply what I thought I knew. It was
a time-consuming process full of mistakes and enigmatic compiler messages.
But the result, BUMP, seems well worth the trouble.
In BUMP you will find applications of most of the C++ techniques. It has
classes with functions, derived classes with inheritance, non-trivial
constructors and destructors, overloaded operators, and even a virtual
function. In the hope that it may be useful to others both as an example
and as a useable product, I have made it publicly available. I hope that
BUMP may help you over the hump to effective use of C++.
BUMP is intentionally a minimal package so that the user can understand it
and extend it in directions of interest to him. Before trying to read the
code, however, it is probably a good idea to use BUMP as it is a bit so
that you know what its functions do before you trying to read them.
Accordingly, I'll first explain how to use it, and then add a few notes on
reading the code.
Use of BUMP
To use BUMP as it is, you write a program in C++. Don't worry if you know
only C and not C++; the code you write can be pure C except for your use of
BUMP's matrix and vector objects. You must compile your program with the
Borland C++ compiler, so the extension of its filename should be ".cpp".
Your program should have among the header lines the line
#include "bump.h";
and the linker command should link in bump.obj. Specific directions for
compiling and linking the example are given below.
Those simple measures are all that is necessary to be able to write a
program like the following, which illustrates some of the capabilities of
BUMP:
Vector a(4),at(1,4),c(4),ct(1,4);
Matrix A(4,4),B(4,4),C(4,4),D(4,1),E(1,1);
// Read in the matrix from an ASCII file and display it
A.ReadA("amatrix");
A.Display("Here is the A matrix:");
// Pre-multiply it by a scalar and divide it by a scalar
B = 2.*A;
B.Display("This is 2.*A.");
B = A/2.;
B.Display("and this is A/2.");
tap();
// Invert it. A ! in front of the matrix is the sign for inverse.
B = !A;
B.Display("and here is B = !A ( A inverse):");
// Multiply two matrices:
C = A*B;
// This next Display will have column width of 15 and 6 decimal places
C.Display("This is A*!A (should be I):",15,6);
// Parentheses work as expected:
B = (A + A)*!A;
B.Display("B = (A+A)*!A. Should be 2I");
C = A*A;
C.Display("A*A:");
C = (A+A)*(A+A);
C.Display("(A+A)*(A+A). Should be 4 times the matrix above.");
// Take the transpose. A ~ in front of a matrix indicates transpose.
B = ~A;
A.Display("Here is A again.");
B.Display("and this is ~A, A transpose.");
// Now some tests with vectors
a.ReadA("avector");
at = ~a;
a.Display("This is the a vector:");
at.Display("and this is its transpose. The difference doesn't show.");
D << a;
D.Display("And this is D << a :");
c = A*a;
c.Display("This is A*a");
ct = ~a*~A;
ct.Display("This is ~a*~A. Should look the same as the above.");
// Tests of the / operator. A/A is A'A computed without taking A'.
B = ~A*A;
B.Display("This is ~A*A");
B = A/A;
B.Display("This is A/A. Should be the same as the above.");
c = A/a;
ct = a/A;
c.Display("This is A/a:");
ct.Display("This is a/A. Should look like the above.");
E = a/a;
B = at/at;
E.Display("This is a/a:");
B.Display("This is at/at:");
printf("\nEnd of test.\n");
}
From this example, we see that BUMP has an extended data type Matrix. A
matrix has a function (ReadA) to read data into it from an ASCII file and
another function (Display) to display the matrix on the screen. The =,
+, -, and * operators have their expected definitions. Parentheses work in
the expected way. A ~ in front of a Matrix calculates its transpose, while
a ! in front of a Matrix calculates its inverse (by a primitive algorithm).
Neither affects the Matrix itself. (Perhaps you don't like ~ for
transposition and ! for inversion. The ' operator, however, is not
available in C++ for overloading; the ^, which I would have preferred for
inversion, is a binary operator in C++ and cannot be used for a unary
process like inversion or transposition. There is, in fact, not much
alternative to ~ and ! for these two unary operations.)
BUMP also has a Vector data type. All the same functions and operators
(except, of course, !) apply to Vectors, and to Vectors and Matrices
together, where dimensions are appropriate.
Pre-multiplication of a Vector or Matrix by a float (a scalar) has been
defined, as has division of a Matrix or Vector by a float. I did not
bother to define post-multiplication of a vector or matrix by a float.
Matrix division does not occur, so the / operator is available to mean
something else. In statistical computations, expressions like X'X or Z'X -
- ~X*X or ~Z*X in BUMP notation -- occur frequently with X' or Z' being
large matrices so that it undesirable to clog up scarce memory by actually
taking the transpose. These products can, of course, be computed directly
from X and Z. That is what Z/X does in BUMP; it computes ~Z*X without ever
creating ~Z.
To convert a Vector, v, to a Matrix, M, one uses
M << v;
Finally, element i of Vector v is denoted by v[i] on either side of the =
sign, while element (i,j) of Matrix M is denoted by M[i][j] on either side
of an =. The Vector elements are range-checked on both sides of an = sign.
Thus, if v has 20 elements and you ask for v[23], you will get an error
message. The range checking on the Matrix element is only on the row index.